/*
	glOutput Team Graphics Implementation
	Elise Eiden, Nick Taylor, Taryn Green
*/

#include "../keyboard_input/KeyboardHandler.h"
#include "../situationControl/Shot.h"
#include "Graphics.h"
// GLOBAL VARIABLES //

//global variables to keep track of window width and height.
//set to initial values for convenience, but we need variables
//for later on in case the window gets resized.
int windowWidth = 512, windowHeight = 512;
float cameraTheta, cameraPhi, cameraRadius; //camera position in spherical coordinates
float cameraX, cameraY, cameraZ;            //camera position in cartesian coordinates
float objX, objY, objZ;
glSituation SITUATION;
KeyboardHandler * myKeyboardHandler; 		// reference to a keybard handler

// END GLOBAL VARIABLES //

// Default Constructor
Graphics::Graphics()
{
 //possibly put pointer to keyboard function
}

//
// void computeArcballPosition(float theta, float phi, float radius,
//                              float &x, float &y, float &z);
//
// This function updates the camera's position in cartesian coordinates based 
//  on its position in spherical coordinates. The user passes in the current phi,
//  theta, and radius, as well as variables to hold the resulting X, Y, and Z values.
//  Those variables for X, Y, and Z values get filled with the camera's position in R3.
//
void computeArcballPosition(float theta, float phi, float radius,
                            float &x, float &y, float &z) {
    x = radius * sinf(theta)*sinf(phi);
    y = radius * -cosf(phi);
    z = radius * -cosf(theta)*sinf(phi);
}


// DRAWING FUNCTIONS

void drawPlayer(glPlayer player)
{
	// draws cube
	glPushMatrix();
		glTranslatef(player.pos[0], player.pos[1], player.pos[2]);
		glRotatef(player.facingHor, 0.0, 1.0, 0.0);
		glRotatef(player.facingVer, 0.0, 0.0, 1.0);
                glutSolidCube( player.length );
	glPopMatrix();
}

void drawRoom(glRoom room)
{
	// draws cube
	glPushMatrix();
		glTranslatef(room.pos[0], room.pos[1], room.pos[2]);
		glutWireCube( room.length );
	glPopMatrix();
}

void drawDrone(glDrone drone)
{
	// draws a drone
	glPushMatrix();
		glTranslatef(drone.pos[0], drone.pos[1], drone.pos[2]);
		glutSolidCube( drone.length );
	glPopMatrix();
}

void drawShot(glShot shot, glPlayer player)
{
	// draws a shot (projectile)
	glPushMatrix();
		glTranslatef(shot.pos[0], shot.pos[1], shot.pos[2]);
		glRotatef(player.facingHor, 0.0, 1.0, 0.0);
		glRotatef(player.facingVer, 0.0, 0.0, 1.0);
		glutSolidCube( shot.length );
	glPopMatrix();
}

void drawSituation()
{
	glPushMatrix();
		glColor3f(1.0,0.0,0.1);
		drawPlayer(SITUATION.player1);

		glColor3f(0.1,0.0,1.0);
		drawPlayer(SITUATION.player2);

		glColor3f(0.9,1.0,0.9);
		drawRoom(SITUATION.room);

		glColor3f(0.1,1.0,0.1);

		for (int i = 0; i < SITUATION.swarm.size(); i++)
 		  drawDrone(SITUATION.swarm[i]);

		// Draw Projectiles
		glColor3f(1.0, 1.0, 0.0);

		for(int i = 0; i < SITUATION.bullets.size(); i++)
		{
		  drawShot(SITUATION.bullets[i], SITUATION.player1);
		}

	glPopMatrix();
}

//
//  void resizeWindow(int w, int h)
//
//      We will register this function as GLUT's reshape callback.
//   When the window is resized, the OS will tell GLUT and GLUT will tell
//   us by calling this function - GLUT will give us the new window width
//   and height as 'w' and 'h,' respectively - so save those to our global vars.
//
void resizeWindow(int w, int h) {
    float aspectRatio = w / (float)h;

    windowWidth = w;
    windowHeight = h;

    //update the viewport to fill the window
    glViewport(0, 0, w, h);

    //update the projection matrix with the new window properties
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0,aspectRatio,0.1,100000);
}

//
//  void updateDisplay()
//
//  GLUT callback for scene rendering. Sets up the modelview matrix, renders
//      a teapot to the back buffer, and switches the back buffer with the
//      front buffer (what the user sees).
//
void updateDisplay()
{
	glDrawBuffer(GL_BACK);
	
	//update the modelview matrix based on the camera's position
	glMatrixMode(GL_MODELVIEW);             //make sure we aren't changing the projection matrix!
	glLoadIdentity();

	gluLookAt(cameraX + objX, cameraY + objY, cameraZ + objZ, objX, objY, objZ, 0.0f, 1.0f, 0.0f);

	//clear the render buffer
	glClearColor(0,0,0,1);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glShadeModel(GL_SMOOTH);

	drawSituation();

	//push the back buffer to the screen
	glutSwapBuffers();
}

void Graphics::acceptKeyboardHandler(KeyboardHandler * KH_ptr)
{
  myKeyboardHandler = KH_ptr;
}


void keyboardInput(unsigned char c, int x, int y)
{

  myKeyboardHandler->key_pressed(c, x, y);
}

//
//  void initScene()
//
//  A basic scene initialization function; should be called once after the
//      OpenGL context has been created. Doesn't need to be called further.
//      Just sets up a few function calls so that our main() is cleaner.
//
void Graphics::initScene(int argc, char **argv) {

	// pasted from original main function
	
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(50,50);
	glutInitWindowSize(windowWidth,windowHeight);
	glutCreateWindow("glOutput Test");

	// setup camera
	cameraRadius = 16.0f;
	cameraTheta = 2.80;
	cameraPhi = 2.0;
	objX = 0.0f;
	objY = 0.0f;
	objZ = 0.0f;
	computeArcballPosition(cameraTheta, cameraPhi, cameraRadius, cameraX, cameraY, cameraZ);
						   
	// register callback functions...
	glutDisplayFunc(updateDisplay);
	glutReshapeFunc(resizeWindow);
	glutKeyboardFunc(keyboardInput);


	// Beginning of original initScene

	glEnable(GL_DEPTH_TEST);

	//this is some code to enable a default light for the scene;
	//feel free to play around with this, but we won't talk about
	//lighting in OpenGL for another couple of weeks yet.
	float lightCol[4] = { 1, 1, 1, 1};
	float ambientCol[4] = {0.0, 0.0, 0.0, 1.0};
	float lPosition[4] = { 5, 5, 5, 1 };
	glLightfv(GL_LIGHT0,GL_POSITION,lPosition);
	glLightfv(GL_LIGHT0,GL_DIFFUSE,lightCol);
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambientCol);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);

	//tell OpenGL not to use the material system; just use whatever we pass with glColor()
	// we probably won't use this.
	glEnable(GL_COLOR_MATERIAL);
	glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

	//tells OpenGL to blend colors across triangles. Once lighting is
	//enabled, this means that objects will appear smoother - if your object
	//is rounded or has a smooth surface, this is probably a good idea;
	//if your object has a blocky surface, you probably want to disable this.
	glShadeModel(GL_SMOOTH);
}

// acts as a timer function
void Graphics::draw(Situation situation)
{
	// copy given situation to render to internal representation
	SITUATION.player1.pos[0] = situation.getPlayer().getXPos();
	SITUATION.player1.pos[1] = situation.getPlayer().getYPos();
	SITUATION.player1.pos[2] = situation.getPlayer().getZPos();
	SITUATION.player1.length = situation.getPlayer().getSize();
	SITUATION.player1.facingHor = situation.getPlayer().getHorFacing();
	SITUATION.player1.facingVer = situation.getPlayer().getVertFacing();

	SITUATION.player2.pos[0] = situation.getOpponent().getXPos();
	SITUATION.player2.pos[1] = situation.getOpponent().getYPos();
	SITUATION.player2.pos[2] = situation.getOpponent().getZPos();
	SITUATION.player2.length = situation.getOpponent().getSize();
	SITUATION.player2.facingHor = situation.getOpponent().getHorFacing();
	SITUATION.player2.facingVer = situation.getOpponent().getVertFacing();

	SITUATION.room.pos[0] = situation.getRoom().getXPos();
	SITUATION.room.pos[1] = situation.getRoom().getYPos();
	SITUATION.room.pos[2] = situation.getRoom().getZPos();
	SITUATION.room.length = situation.getRoom().getWidth();
	

  		
	SITUATION.swarm.clear();
	for(int i = 0; i < situation.getSwarmSize(); i++){
	  struct glDrone temp;
	  Drone currentDrone = situation.getDrone(i);
  	  temp.pos[0] = currentDrone.getXPos();
	  temp.pos[1] = currentDrone.getYPos();
	  temp.pos[2] = currentDrone.getZPos();
	  temp.length = currentDrone.getSize();
	  SITUATION.swarm.push_back(temp);
	}

	SITUATION.bullets.clear();
	for(int i = 0; i < situation.getNumShots(); i++)
	{
	  struct glShot temp;
	  Shot currentShot = situation.getShot(i);
	  temp.pos[0] = currentShot.getXPos();
	  temp.pos[1] = currentShot.getYPos();
	  temp.pos[2] = currentShot.getZPos();
	  temp.length = currentShot.getSize();
	  SITUATION.bullets.push_back(temp);
	}

	glutPostRedisplay();
	
	glutMainLoopEvent();
}

